`include "timescale.v"

module onu_tx (
	input 		   reset,
	input 		   Gtx_clk,						//GMII interface

	output  	   Tx_en, 
	output 		   Tx_er,		
	output reg [7:0] Txd,

	output [5:0]   tx_state,
	output [15:0]  tx_byte_cnt,
	
	input 		   h_tx_data_ready,		//the ~empty of asyncfifo	
	input  [7:0]   h_tx_data,			//tx data from host	, use together with h_tx_data_ready


	output 		   h_tx_data_rd			//the read signal !
	// //from register
	// input  [15:0]  MinFL, MaxFL,				 //min/max frame length
	// input  [3:0]   IPGT,						 //back to back transmit inter packet gap parameter 

	//
	// input 		   TxUnderRun,					//should always be 0 ??

	// output 		   TxDone, TxRetry, TxAbort, TxUsedData,	//need TxDone !!!
	// output 		   WillTransmit,
	// output 		   StartTxDone, StartTxAbort,
	//interface for mac control
	// input 		   TxStartFrm, TxEndFrm,
	);

reg 			  StateIdle, StatePreamble, StateData, StatePAD, StateFCS, StateIPG;
wire 			  StartIdle, StartPreamble, StartData, StartPAD, StartFCS, StartIPG;

wire 			  StateSFD;

assign 			  tx_state[5:0] = {StateIdle, StatePreamble, StateData, StatePAD, StateFCS, StateIPG};
								   
assign 			  h_tx_data_rd = StartData | StateData;


// ==================== start of tx counters ====================
reg 	  [15:0]  ByteCnt;
wire 			  ByteCntMax;

assign 			  ResetByteCnt = StartIPG | StartPreamble | StartData | StartFCS;		//not reset the counter when StartPAD
assign 			  IncrementByteCnt = ~ResetByteCnt & (StateIPG | StatePreamble | StateData | StatePAD | StateFCS) & ~ByteCntMax;
// StatePreamble & NibCntEq15 | StateJam & NibCntEq7 | StateIdle | StartDefer | StartIPG | StartFCS

// assign 			  tx_byte_cnt = ByteCnt;
assign 			  tx_byte_cnt = StartData ? 16'd0 : StateData ? ByteCnt+16'd1 : 16'hx;

//ByteCnt
always@(posedge Gtx_clk or posedge reset)
begin
  if(reset)
	ByteCnt <= 16'd0;
  else if(ResetByteCnt)
	ByteCnt <= 16'd0;
  else if(IncrementByteCnt)
	ByteCnt <= ByteCnt + 16'd1;
end

assign 			  ByteCntMax  = &ByteCnt;
//////////////////// end of tx counters ////////////////////

wire 			  MaxFrame;
// assign 			  MaxFrame 	  = ByteCnt == MaxFL;
assign 			  MaxFrame 	  = ByteCnt == 16'h600;

wire 			  ByteCntEq3, ByteCntEq6, ByteCntEq7, ByteCntEq8;
assign 			  ByteCntEq3  = &ByteCnt[1:0];
assign 			  ByteCntEq6  = ByteCnt[2:0] == 3'd6;
assign 			  ByteCntEq7  = &ByteCnt[2:0];
assign 			  ByteCntEq8  = ByteCnt[3:0] == 4'd8;

assign 			  StateSFD 	  = StatePreamble & ByteCntEq7;
  
wire 			  ByteMinFl;
// assign 			  ByteMinFl   = ByteCnt >= MinFL-16'd4-1;
assign 			  ByteMinFl   = ByteCnt >= 16'h40-16'd4-1;

// ==================== start of the tx fsm ====================

// assign 			  StartIdle   = StateIPG & (ByteCnt[3:0] >= IPGT);
assign 			  StartIdle   = StateIPG & (ByteCnt[3:0] >= 4'd12);
// assign 			  StartPreamble = StateIdle & TxStartFrm;
assign 			  StartPreamble = StateIdle & h_tx_data_ready;
assign 			  StartData   = StatePreamble & ByteCntEq7;
assign 			  StartPAD 	  = StateData & ~h_tx_data_ready & ~ByteMinFl;
assign 			  StartFCS 	  = (StateData & ~h_tx_data_ready & ByteMinFl) | (StatePAD & ByteMinFl);
assign 			  StartIPG 	  = StateData & MaxFrame | StateFCS & (ByteCntEq3 | MaxFrame) ;

always@(posedge Gtx_clk or posedge reset)
begin
  if(reset)
  begin
	StateIPG <= 1'b1;
	StateIdle <= 1'b0;
	StatePreamble <= 1'b0;
	StateData <= 1'b0;
	StatePAD <= 1'b0;
	StateFCS <= 1'b0;
  end
  else
	if(StartPreamble)
	  StateIdle <= 1'b0;
	else if(StartIdle)
	  StateIdle <= 1'b1;
  
  if(StartData)
	StatePreamble <= 1'b0;
  else if(StartPreamble)
	StatePreamble <= 1'b1;
  
  if(StartPAD | StartFCS)
	StateData <= 1'b0;
  else if(StartData)
	StateData <= 1'b1;

  if(StartFCS)
	StatePAD <= 1'b0;
  else if(StartPAD)
	StatePAD <= 1'b1;

  if(StartIPG)
	StateFCS <= 1'b0;
  else if(StartFCS)
	StateFCS <= 1'b1;
  
  if(StartIdle)
  	StateIPG <= 1'b0;
  else if(StartIPG)
  	StateIPG <= 1'b1;
end
//////////////////// end of tx fsm ////////////////////

// ==================== start of CRC ====================
wire 	  [31:0]  Crc;
wire 			  CrcErr;
wire 			  enable_crc, init_crc;

// assign 			  enable_crc  = Tx_en & (StateData & | StatePAD);

assign 			  enable_crc  = ~StateFCS;				//when in StateFCS, just shift the CRC regs
assign 			  init_crc 	  = Tx_en & StartData | StateIdle; //or Tx_en & StatePreamble

crc32 crc_tx(
	.clk					(Gtx_clk),
	.reset 					(reset),
	.Data					(Txd),
	.enable					(enable_crc),
	.init					(init_crc),
	.Crc					(Crc),
	.CrcErr					(CrcErr) );
//////////////////// end of CRC ////////////////////

// ==================== transmit bytes ====================
assign 			  Tx_en 	  = StatePreamble | StateData | StatePAD | StateFCS ;
assign 			  Tx_er 	  = 1'b0;
// reg 	  [7:0]   Txd_d;

// always@(posedge Gtx_clk)
//   Tx_en <= Tx_en_d;

always@(*)
begin
  if(StatePreamble)
	Txd <= ByteCntEq7 ? 8'hD5 : 8'h55;			//Txd not need sync ??
  else if(StateData)
	// Txd <= $random;
	Txd <= h_tx_data;			//the frame data
  else if(StatePAD)
	Txd <= 8'h00;
  else if(StateFCS)
	Txd <= {~Crc[24], ~Crc[25], ~Crc[26], ~Crc[27], ~Crc[28], ~Crc[29], ~Crc[30], ~Crc[31]};
  else 
	Txd <= 8'hxx;
end

// Txd = Txd_d@(clk)  or h_rx_data;
// always@(posedge Gtx_clk)
//   Txd <= Txd_d;
// always@(*)
//   Txd 		= Txd_d;

//////////////////// end of transmit ////////////////////

// //for debug
// initial Txd <= 8'd3;
// always@(posedge Gtx_clk)
// begin
//   Txd <= Txd + 8'd1;
// end



endmodule // onu_tx
